
#include "DrmPageFlip.h"

static struct drm_device *extern_buf;

DrmPageFlip::DrmPageFlip()
{
	extern_buf = this->buf;
}

static void drm_destroy_fb(int fd, struct drm_device *bo)
{
	struct drm_mode_destroy_dumb destroy = {};

	drmModeRmFB(fd, bo->fb_id);
	
	munmap(bo->vaddr, bo->size);
	
	destroy.handle = bo->handle;

	drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
}

DrmPageFlip::~DrmPageFlip()
{
    drm_destroy_fb(this->m_fd, &buf[0]);
	drm_destroy_fb(this->m_fd, &buf[1]);
    drm_destroy_fb(this->m_fd, &buf[2]);
	drmModeFreeConnector(this->m_conn);
	drmModeFreeResources(this->m_res);
	close(this->m_fd);
}

int show_inmage_index = 0;
static void drm_page_flip_handler(int fd, uint32_t frame,
				    uint32_t sec, uint32_t usec,
				    void *data)
{
	uint32_t crtc_id = *(uint32_t *)data;

    if (show_inmage_index > 2 || show_inmage_index < 0) return;

	drmModePageFlip(fd, crtc_id, extern_buf[show_inmage_index].fb_id,
			DRM_MODE_PAGE_FLIP_EVENT, data);
}

static int drm_create_fb(int fd, struct drm_device *bo)
{
		/* create a dumb-buffer, the pixel format is XRGB888 */
	bo->create.width = bo->width;
	bo->create.height = bo->height;
	bo->create.bpp = 32;

	/* handle, pitch, size will be returned */
	drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &bo->create);

	/* bind the dumb-buffer to an FB object */
	bo->pitch = bo->create.pitch;
	bo->size = bo->create.size;
	bo->handle = bo->create.handle;
	drmModeAddFB(fd, bo->width, bo->height, 24, 32, bo->pitch,
			   bo->handle, &bo->fb_id);
	
	//每行占用字节数，共占用字节数，MAP_DUMB的句柄
	printf("pitch = %d ,size = %d, handle = %d \n",bo->pitch,bo->size,bo->handle);

	/* map the dumb-buffer to userspace */
	bo->map.handle = bo->create.handle;
	drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &bo->map);
	printf("mmap\n");
	bo->vaddr = (uint32_t *)mmap(0, bo->create.size, PROT_READ | PROT_WRITE,
			MAP_SHARED, fd, bo->map.offset);

	/* initialize the dumb-buffer with white-color */
	printf("memset:bo->size[%d],bo->create.size[%d],map.offset[%d]\n", bo->size, bo->create.size, bo->map.offset);
	// memset(bo->vaddr, 0x00,bo->size);
	printf("return\n");
	return 0;
}

int DrmPageFlip::init(void)
{
    m_ev.version = DRM_EVENT_CONTEXT_VERSION;
	m_ev.page_flip_handler = drm_page_flip_handler;

    //打开drm设备，设备会随设备树的更改而改变,多个设备时，请留一下每个屏幕设备对应的drm设备
	this->m_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
    if(this->m_fd < 0){
        printf("wrong\n");
        return 0;
    }

	//获取drm的信息
	m_res = drmModeGetResources(this->m_fd);
	this->m_crtc_id = this->m_res->crtcs[0];
	this->m_conn_id = this->m_res->connectors[0];
	//打印CRTCS,以及conneter的id
	printf("crtc = %d , conneter = %d\n",this->m_crtc_id, this->m_conn_id);

	this->m_conn = drmModeGetConnector(this->m_fd, this->m_conn_id);
	buf[0].width = this->m_conn->modes[0].hdisplay;
	buf[0].height = this->m_conn->modes[0].vdisplay;

	buf[1].width = this->m_conn->modes[0].hdisplay;
	buf[1].height = this->m_conn->modes[0].vdisplay;

    buf[2].width = this->m_conn->modes[0].hdisplay;
	buf[2].height = this->m_conn->modes[0].vdisplay;

	//打印屏幕分辨率
	printf("width = %d , height = %d\n",buf[0].width,buf[0].height);

	//创建framebuffer层
	drm_create_fb(this->m_fd, &buf[0]);
	drm_create_fb(this->m_fd, &buf[1]);
    drm_create_fb(this->m_fd, &buf[2]);
	printf("drm_create_fb ok.\n");

    /* 显示黑色屏幕 */
    for(int i=0;i<buf[0].width*buf[0].height;i++)
		buf[0].vaddr[i] = 0xFF0000;

	for(int i=0;i<buf[0].width*buf[0].height;i++)
		buf[1].vaddr[i] = 0x00FF00;

	for(int i=0;i<buf[0].width*buf[0].height;i++)
		buf[2].vaddr[i] = 0x0000FF;

    drmModeSetCrtc(this->m_fd, this->m_crtc_id, buf[0].fb_id,
        0, 0, &this->m_conn_id, 1, &this->m_conn->modes[0]);

	drmModePageFlip(this->m_fd, this->m_crtc_id, buf[0].fb_id,
			DRM_MODE_PAGE_FLIP_EVENT, &this->m_crtc_id);

	/* Test */
	printf("----------------------------- test -----------------------------\n");
	sleep(1);
	show_image(3);
	printf("test show_image(3)\n");

	sleep(1);
	show_image(2);
	printf("test show_image(3)\n");

	sleep(1);
	show_image(1);
	printf("test show_image(3)\n");

	return 0;
}

int DrmPageFlip::show_image(int num)
{
    show_inmage_index = num;
    drmHandleEvent(this->m_fd, &this->m_ev);
	
	return 0;
}